package com.log4j.appender

import com.log4j.entity.TemplateAppenderBuilder
import org.apache.logging.log4j.core.Core
import org.apache.logging.log4j.core.Filter
import org.apache.logging.log4j.core.Layout
import org.apache.logging.log4j.core.LogEvent
import org.apache.logging.log4j.core.appender.AbstractAppender
import org.apache.logging.log4j.core.appender.RollingFileAppender
import org.apache.logging.log4j.core.config.AbstractConfiguration
import org.apache.logging.log4j.core.config.Configuration
import org.apache.logging.log4j.core.config.Property
import org.apache.logging.log4j.core.config.plugins.Plugin
import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory
import org.apache.logging.log4j.core.filter.CompositeFilter
import org.apache.logging.log4j.core.filter.LevelMatchFilter
import org.apache.logging.log4j.kotlin.logger
import java.io.Serializable
import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KProperty1
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.jvm.isAccessible

@Plugin(
    name = TemplateAppender.PLUGIN_NAME,
    category = Core.CATEGORY_NAME,
    printObject = true
)
class TemplateAppender(
    val builder: TemplateAppenderBuilder<*>,
    name: String,
    layout: Layout<out Serializable>,
    ignoreExceptions: Boolean,
    properties: Array<out Property>,
    val configuration: Configuration
) : AbstractAppender(name, null, layout, ignoreExceptions, properties) {

    val templateAppenders = ConcurrentHashMap<String, RollingFileAppender>()

    @Suppress("UNCHECKED_CAST")
    val filterType: KProperty1<CompositeFilter, Array<Filter>> = CompositeFilter::class.declaredMemberProperties.find {
        it.name == "filters"
    }?.also {
        it.isAccessible = true
    } as KProperty1<CompositeFilter, Array<Filter>>

    override fun append(event: LogEvent) {
//        println("-------------------------****************")
        var fileName = builder.fileName
        var filePattern = builder.filePattern
        if (configuration is AbstractConfiguration) {
            fileName = configuration.configurationStrSubstitutor.replace(event, fileName)
            filePattern = configuration.configurationStrSubstitutor.replace(event, filePattern)
        }
//        println(fileName)
//        println(filePattern)
//        println(event)

        if (fileName.isNullOrEmpty() || filePattern.isNullOrEmpty()) {
            logger().warn("错误的日志配置信息, builder = $builder")
            return
        }

        val key = "${builder.name}-${event.level.name()}${event.marker?.name?.let { "-$it" } ?: ""}"

        var appender = templateAppenders[key]
        if (appender == null) {

            var filter = builder.filter
            if (filter is CompositeFilter) {
                val file = filterType.get(filter)
                val newFilter = arrayOfNulls<Filter>(file.size)
                file.forEachIndexed { i, t ->
                    if (t is LevelMatchFilter) {
                        newFilter[i] = LevelMatchFilter.newBuilder().setLevel(event.level).build()
                    } else newFilter[i] = t
                }
                filter = CompositeFilter.createFilters(newFilter)
            } else {
                if (filter is LevelMatchFilter) {
                    filter = LevelMatchFilter.newBuilder().setLevel(event.level).build()
                }
            }

            appender = RollingFileAppender.Builder().also {
                it.configuration = configuration
                it.name = key
                it.layout = builder.layout
                it.filter = filter
                it.propertyArray = builder.propertyArray
                it.isIgnoreExceptions = builder.isIgnoreExceptions
                it.isBufferedIo = builder.isBufferedIo
                it.isImmediateFlush = builder.isImmediateFlush
                it.bufferSize = builder.bufferSize

                it.withFilePattern(filePattern)
                    .withFileName(fileName)
                    .withAppend(builder.append)
                    .withCreateOnDemand(builder.createOnDemand)
                    .withFilePermissions(builder.filePermissions)
                    .withFileOwner(builder.fileOwner)
                    .withFileGroup(builder.fileGroup)
                    .withStrategy(builder.strategy)
                    .withPolicy(builder.policy)
                    .withAdvertise(builder.advertise)
                    .withAdvertiseUri(builder.advertiseUri)

            }.build()
            appender.run {
                templateAppenders[key] = this
            }
        }
        appender?.append(event)

    }

    companion object {

        const val PLUGIN_NAME: String = "TemplateAppender"


        /**
         * Creates a new Builder.
         *
         * @return a new Builder.
         * @since 2.7
         */
        @PluginBuilderFactory
        @JvmStatic
        fun <B : TemplateAppenderBuilder<B>> newBuilder(): B? {
            return TemplateAppenderBuilder<B>().asBuilder()
        }


    }


}